﻿
namespace TinyMceWrapper.Web.UI
{

    #region [ Imports ]

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Configuration;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Text;
    using System.Web.UI;
    using TinyMceWrapper.Web.Configuration;
    using TinyMceWrapper.Web.UI.Design;

    #endregion

    [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Mce"), ToolboxData("<{0}:TinyMceScriptManager runat=\"server\" />"), Designer(typeof(EmptyDesigner))]
    public class TinyMceScriptManager : Control
    {

        #region [ Local Fields ]

        private readonly Dictionary<string, StringSet> _displayModes = new Dictionary<string, StringSet>();

        #endregion

        #region [ Public Static Methods ]

        /// <summary>
        /// Gets current tiny mce editor script manager control.
        /// </summary>
        /// <param name="page">The current page.</param>
        /// <returns>Returns an instance of TinyMceWrapper.Web.UI.WebControls.TinyMceScriptManager.</returns>
        public static TinyMceScriptManager GetCurrent(Page page)
        {
            return page.Items[typeof(TinyMceScriptManager)] as TinyMceScriptManager;
        }

        #endregion

        #region [ Public Virtual Methods ]

        /// <summary>
        /// Registers a tiny mce editor to generate initialize script.
        /// </summary>
        /// <param name="clientId">The tiny mce editor client id.</param>
        /// <param name="displayMode">The tiny mce editor display mode.</param>
        public virtual void RegisterEditor(string clientId, string displayMode)
        {
            if (!this._displayModes.ContainsKey(displayMode))
                this._displayModes.Add(displayMode, new StringSet() { clientId });
            else
                this._displayModes[displayMode].Add(clientId);
        }

        /// <summary>
        /// Gets registered TinyMce editors in a specified display mode.
        /// </summary>
        /// <param name="displayMode">The display mode name.</param>
        /// <returns>Returns a string array of TinyMce editors' id.</returns>
        public virtual string[] GetEditors(string displayMode)
        {
            return this._displayModes[displayMode];
        }

        #endregion

        #region [ Overriden Members ]

        /// <summary>
        /// Raises the System.Web.UI.Control.Init event.
        /// </summary>
        /// <param name="e">An System.EventArgs object that contains the event data.</param>    
        protected override void OnInit(EventArgs e)
        {
            if (!this.DesignMode && this.Page != null)
                this.Page.Items.Add(typeof(TinyMceScriptManager), this);

            base.OnInit(e);
        }

        /// <summary>
        /// Raises the System.Web.UI.Control.PreRender event.
        /// </summary>
        /// <param name="e">An System.EventArgs object that contains the event data.</param>
        [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
        protected override void OnPreRender(EventArgs e)
        {
            // Get TinyMce editor configuration section.
            TinyMceSection section = ConfigUtil.TinyMce;

            // Include TinyMce editor script file to the page.
            Page.ClientScript.RegisterClientScriptInclude(typeof(TinyMceScriptManager), "TinyMCE", this.ResolveClientUrl(string.Format(CultureInfo.InvariantCulture, "{0}/tiny_mce{1}.js", section.ScriptPath, (!string.IsNullOrEmpty(section.ScriptMode) ? "_" + section.ScriptMode : null))));

            // Generate script for each display mode.
            foreach (KeyValuePair<string, StringSet> item in this._displayModes)
            {
                // Get display mode.
                TinyMceDisplayMode displayMode = section.DisplayModes[item.Key];
                // Throw exception while display mode does not exist.
                Utilities.ThrowOnFailure<ConfigurationErrorsException>(displayMode == null, string.Format(CultureInfo.CurrentCulture, Strings.TinyMce_display_mode_not_found, item.Key), section.ElementInformation.Source, section.ElementInformation.LineNumber);
                // Declare a field of StringBuilder to store output script.
                StringBuilder output = new StringBuilder();
                // Declare a field of StringSet to store options collection.
                StringSet options = new StringSet();
                // Write begin of script.
                output.AppendFormat("tinyMCE.init({{ elements: \"{0}\", ", string.Join(", ", item.Value));
                // Add options to StringSet.
                IEnumerator enumerator = displayMode.Options.GetEnumerator();
                while(enumerator.MoveNext())
                {
                    TinyMceOption opt = (TinyMceOption)enumerator.Current;

                    if (opt.Value is string)
                        // Write a string option.
                        options.AddWithFormat("{0}: \"{1}\"", opt.Key, (opt.UrlOption ? this.ResolveClientUrl((string)opt.Value) : opt.Value));
                    else
                        // Write a boolean or numeric option.
                        options.AddWithFormat("{0}: {1}", opt.Key, opt.Value.ToString().ToLowerInvariant());
                }
                // Join added options from StringSet.
                output.Append(string.Join(", ", options));
                // Write end of script.
                output.Append(" });\r\n");
                // Register generated script as a startup script.
                this.Page.ClientScript.RegisterStartupScript(typeof(TinyMceScriptManager), "TinyMce_" + displayMode.Name, output.ToString(), true);
            }

            base.OnPreRender(e);
        }

        #endregion

        internal sealed class StringSet : StringCollection
        {

            #region [ Public Methods ]

            public int AddWithFormat(string value, params object[] args)
            {
                return this.Add(string.Format(CultureInfo.InvariantCulture, value, args));
            }

            #endregion

            #region [ Conversion Operators ]

            public static implicit operator string[](StringSet set)
            {
                return (string[])new ArrayList(set).ToArray(typeof(string));
            }

            #endregion

        }

    }

}
